# -*- coding: utf-8 -*-
import networkx as nx
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import collections
import json
import sys
import random
import math
import copy
from model import anagraphdistance

reload(sys)
sys.setdefaultencoding('utf8')

class NodeCluster:
    def __init__(self, distance_gate, similarity_gate, algorithm_type = 1):
        self.node_list          = []
        self.anagraphs_dict     = collections.OrderedDict()
        self.distance_gate      = distance_gate
        self.similarity_gate    = similarity_gate
        self.append_list        = []
        self.ana_distance       = anagraphdistance.AnagraphDistance(algorithm_type)

    def setClusterNodes(self, node_list):
        self.node_list = self.__reshapeData(node_list)

    def startCluster(self):
        if len(self.node_list) == 0:
            print 'please set node list first'

        self.__initCluster()

        last_cluster_count  = 0
        cycle_count         = 0
        cycle_limited       = 5000

        while True:
            self.__mergeOneCluster()

            self.__adjustCluster()

            tmp_cluster_count = self.__calculateClusterCount()

            if last_cluster_count == tmp_cluster_count or cycle_count > cycle_limited:
                break

            last_cluster_count = tmp_cluster_count
            cycle_count += 1

        return self.anagraphs_dict

    def __reshapeData(self, medicine_data):
        for item_ana in medicine_data:
            if item_ana.has_key('vector'):
                tmp_vector = np.array(item_ana['vector'])
                tmp_vector = tmp_vector.reshape(1, tmp_vector.size)
                item_ana['vector'] = list(tmp_vector[0])

        return medicine_data

    def __initCluster(self):
        # 初始化合并的集合，将自身放入其中
        for node_item in self.node_list:
            if not self.anagraphs_dict.has_key(node_item['name']):
                self.anagraphs_dict[node_item['name']] = []
            self.anagraphs_dict[node_item['name']].append(node_item['name'])

        tmp_name_list = []
        # 根据药物构成的集合相似度来判断是否放入初始的方剂集合中
        for node_item_1 in self.node_list:
            tmp_name_list.append(node_item_1['name'])

            for node_item_2 in self.node_list:
                if node_item_2['name'] not in tmp_name_list:
                    distance = self.ana_distance.getRedisDistance(node_item_1['name'], node_item_2['name'])

                    if distance < self.distance_gate:
                        self.anagraphs_dict[node_item_1['name']].append(node_item_2['name'])
                        self.anagraphs_dict[node_item_2['name']].append(node_item_1['name'])

        for (ana_name, ana_list) in self.anagraphs_dict.items():
            ana_list = list(set(ana_list))

    def __mergeOneCluster(self):
        tmp_name_list = []

        merge_dict = {}
        for (ana_name_1, ana_list_1) in self.anagraphs_dict.items():
            merge_dict[ana_name_1] = []
            tmp_name_list.append(ana_name_1)
            for (ana_name_2, ana_list_2) in self.anagraphs_dict.items():
                if ana_name_2 not in tmp_name_list:
                    if len(ana_list_1) > 0 and len(ana_list_2) > 0:
                        similarity = self.__calculateJaccardSimilarity(ana_list_1, ana_list_2)
                        if similarity >= self.similarity_gate:
                            merge_dict[ana_name_1].append(ana_name_2)

        for (source_name, target_list) in merge_dict.items():
            if len(target_list) > 0:
                for target_name in target_list:
                    self.anagraphs_dict[source_name].extend(self.anagraphs_dict[target_name])
                    self.anagraphs_dict[target_name] = [target_name]
                self.anagraphs_dict[source_name] = list(set(self.anagraphs_dict[source_name]))

    def __adjustCluster(self):
        # 调整集合中不满足算法条件的项
        for (ana_name, ana_list) in self.anagraphs_dict.items():
            tmp_list = []
            tmp_list.append(ana_name)

            for des_name in ana_list:
                if ana_name != des_name:
                    distance = self.ana_distance.getRedisDistance(ana_name, des_name)
                    if distance >= 1.0 - (1.0 - self.distance_gate) * self.similarity_gate:
                        continue
                    else:
                        tmp_list.append(des_name)

            ana_list = tmp_list

    def __calculateJaccardSimilarity(self, list_1, list_2):
        insect_names    = list(set(list_1) & set(list_2))
        union_names     = list(set(list_1) | set(list_2))

        similarity      = float(len(insect_names)) / len(union_names)

        return similarity

    def __calculateClusterCount(self):
        cluster_count = 0

        for (ana_name, ana_list) in self.anagraphs_dict.items():
            if len(ana_list) > 1:
                cluster_count += 1

        return cluster_count

    def getNodes(self):
        return self.node_list
